#include "tools.h"
#include "libheader.h"

/*****************************************************************************/
/*                                                                           */
/* Constructor and Destructor                                                */
/*                                                                           */
/*****************************************************************************/

Tools::Tools(){
}

Tools::~Tools(){
}

/*****************************************************************************/
/*                                                                           */
/* Pub 1 Access Attributes                                                   */
/*                                                                           */
/*****************************************************************************/
  
/*****************************************************************************/
/*                                                                           */
/* Pub 2 Algorithms                                                          */
/*                                                                           */
/*****************************************************************************/

//calculate C(n,r)                          
long double Tools::choose(const long n, const long r) {
  if(r>n)
    return 0;
    
  if(r==0||r==n)
    return 1;
  
  long double total=(long double)n/r;
  for(long i=1;i<r;i++)
    total=total*(n-i)/(r-i);
  return total;
}

//convert an integer to a string
string Tools::intToStr(int i) {
  ostringstream oss;
  oss << i;
  return oss.str();
}

//Hamming distance
int Tools::hamDist(const char* s1, const char* s2, const int testLength) {
  int error=0;
  for(int i=0;i<testLength;i++)
    if(s1[i]!=s2[i])
      error++;
  return error;
}

int Tools::hamDistR(const char* s1, const char* s2, const int testLength) {
  int error=0;
  for(int i=0;i<testLength;i++)
    if(s1[i]!=s2[i])
      error++;
  
  int error2=0;
  for(int i=0;i<testLength;i++){
    switch(s1[i]){
      case 'A':
        if(s2[testLength-i-1]!='T')
          error2++;
        break;
      case 'C':
        if(s2[testLength-i-1]!='G')
          error2++;
        break;
      case 'G':
        if(s2[testLength-i-1]!='C')
          error2++;
        break;
      case 'T':
        if(s2[testLength-i-1]!='A')
          error2++;
        break;
    }
  }
  if(error<error2)
    return error;
  else
    return error2;
}


int Tools::hamDist_shift(const char* s1, const char* s2, const int testLength
                         , const int max_shift){
  int min_error=INT_MAX;
  for(int pos=max_shift;pos>0;pos--){
    int error=pos;
    
    for(int i=0;i<testLength-pos;i++){
      if(s1[i]!=s2[i+pos])
        error++;
    }
    if(error < min_error)
      min_error=error;
  }
  for(int pos=max_shift;pos>0;pos--){
    int error=pos;
    
    for(int i=0;i<testLength-pos;i++){
      if(s1[i+pos]!=s2[i])
        error++;
    }
    if(error < min_error)
      min_error=error;
  }
  
  int error=0;
  for(int i=0;i<testLength;i++){
    if(s1[i]!=s2[i])
      error++;
  }
  if(error < min_error)
    min_error=error;
    
  return min_error;
}

int Tools::matchChar(const char* s1, const char* s2, const int l1, const int l2){
  int max_match=0;

/*  
  for(int i=0;i<l1;i++){
    for(int j=0;j<l2;j++){
      int tmp_match=0;
      int max_test=l1-i;
      if(l2-j<max_test){
        max_test=l2-j;  
      }
      
      for(int k=0;k<max_test;k++){
        if(s1[i+k]==s2[j+k]){
          tmp_match++;
        } else {
          break;
        }
      }
    
      if(tmp_match > max_match){
        max_match=tmp_match;
      }
    }
  }
  return max_match;
*/
  
  int* score = new int[(l1+1)*(l2+1)];
  int row_len=l2+1;
  for(int i=0;i<=l1;i++){
    score[i*row_len]=0;
  }
  for(int j=0;j<=l2;j++){
    score[j]=0;
  }
  

  for(int i=0;i<l1;i++){
    for(int j=0;j<l2;j++){
      if(s1[i]==s2[j]){
        score[(i+1)*row_len+(j+1)]=max(score[i*row_len+j]+1,0);
        if(score[(i+1)*row_len+(j+1)] > max_match){
          max_match=score[(i+1)*row_len+(j+1)];
        }
      } else {
        score[(i+1)*row_len+(j+1)]=max(score[i*row_len+j],0);
      }
    }  
  }
  
  delete[] score;
  return max_match;
}






//find all combination of (comArrSize-comArrPos) balls into
//(m-combinationArr[comArrPos-1]-1) boxes
void Tools::combination(long* combinationArr, const long comArrSize, const long comArrPos
                        , const long m, vector<long*> &comArrVec) {
  if(comArrPos==comArrSize) {
    long* tempComArr=new long[comArrSize];
    for(long i=0;i<comArrSize;i++)
      tempComArr[i]=combinationArr[i];
    comArrVec.push_back(tempComArr);
  } else {
    //check if no of boxes = no of balls
    long boxLeave=m;
    long boxStart=0;
    long noOfBalls=comArrSize-comArrPos;
    
    if(comArrPos!=0){
      boxStart=combinationArr[comArrPos-1]+1;
      boxLeave-=boxStart;
    }
    
    if(boxLeave==noOfBalls) {
      for(long i=0;i<noOfBalls;i++)
        combinationArr[comArrPos+i]=boxStart+i;
      
      long* tempComArr=new long[comArrSize];
      for(long i=0;i<comArrSize;i++)
        tempComArr[i]=combinationArr[i];
      comArrVec.push_back(tempComArr);
    } else {
      for(long i=0;i<=boxLeave-noOfBalls;i++) {
        combinationArr[comArrPos]=boxStart+i;
        combination(combinationArr, comArrSize, comArrPos+1,m,comArrVec);
      }
    }
  }
}

void Tools::longToPattern(char* pattern, const long patternLength
                          , const unsigned long number) {
  unsigned long tempNum=number;
  for(long i=0;i<patternLength;i++) {
    switch(tempNum%4) {
      case 0:
        pattern[i]='A';
        break;
      case 1:
        pattern[i]='C';
        break;
      case 2:
        pattern[i]='G';
        break;
      case 3:
        pattern[i]='T';
        break;
    }
    tempNum/=4;
  }
  pattern[patternLength]=0;
}

unsigned long Tools::patternToLong(const char* pattern, const long patternLength) {
  unsigned long number=0;
  for(int i=0;i<patternLength;i++) {
    number*=4;
    switch(pattern[patternLength-1-i]){
      case 'A':
        //number+=0;
        break;
      case 'C':
        number+=1;
        break;
      case 'G':
        number+=2;
        break;
      case 'T':
        number+=3;
        break;
      default:
        return ULONG_MAX;
        break;  
    }
  }
  return number;
}

/*****************************************************************************/
/*                                                                           */
/* Pri 1 Algorithms                                                          */
/*                                                                           */
/*****************************************************************************/

